
/**
  ******************************************************************************
  * Copyright 2021 The Microbee Authors. All Rights Reserved.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  * 
  * http://www.apache.org/licenses/LICENSE-2.0
  * 
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  * 
  * @file       SoftSerial.c
  * @author     baiyang
  * @date       2022-6-11
  ******************************************************************************
  */

/*----------------------------------include-----------------------------------*/
#include "SoftSerial.h"
#include <rtconfig.h>
/*-----------------------------------macro------------------------------------*/

/*----------------------------------typedef-----------------------------------*/

/*---------------------------------prototype----------------------------------*/
static uint8_t cal_parity(uint8_t ch);
/*----------------------------------variable----------------------------------*/

/*-------------------------------------os-------------------------------------*/

/*----------------------------------function----------------------------------*/
void softserial_ctor(soft_serial_t softserial, uint32_t _baudrate, enum serial_config _config)
{
    softserial->baudrate = _baudrate;
    softserial->config = _config;
    softserial->half_bit = ((1000000U / softserial->baudrate)/2);

    switch (softserial->config) {
    case SERIAL_CONFIG_8N1:
    case SERIAL_CONFIG_8N1I:
        softserial->data_width = 8;
        softserial->byte_width = 10;
        softserial->stop_mask = 0x200;
        break;
    case SERIAL_CONFIG_8E2I:
        softserial->data_width = 9;
        softserial->byte_width = 12;
        softserial->stop_mask = 0xC00;
        break;
    }
}

bool softserial_process_pulse(soft_serial_t softserial, uint32_t width_high, uint32_t width_low, uint8_t *byte)
{
    // convert to bit widths, allowing for a half bit error
    uint16_t bits_high = ((width_high+softserial->half_bit)*softserial->baudrate) / 1000000;
    uint16_t bits_low = ((width_low+softserial->half_bit)*softserial->baudrate) / 1000000;

    softserial->byte_timestamp_us = softserial->timestamp_us;
    softserial->timestamp_us += (width_high + width_low);

    if (bits_high == 0 || bits_low == 0) {
        // invalid data
        goto reset;
    }

    if (bits_high >= softserial->byte_width) {
        // if we have a start bit and a stop bit then we can have at
        // most 9 bits in high state for data. The rest must be idle
        // bits
        bits_high = softserial->byte_width-1;
    }

    if (softserial->state.bit_ofs == 0) {
        // we are in idle state, waiting for first low bit. swallow
        // the high bits
        bits_high = 0;
    }

    softserial->state.byte |= ((1U<<bits_high)-1) << softserial->state.bit_ofs;

    softserial->state.bit_ofs += bits_high + bits_low;

    if (softserial->state.bit_ofs >= softserial->byte_width) {
        // check start bit
        if ((softserial->state.byte & 1) != 0) {
            goto reset;
        }
        // check stop bits
        if ((softserial->state.byte & softserial->stop_mask) != softserial->stop_mask) {
            goto reset;
        }
        if (softserial->config == SERIAL_CONFIG_8E2I) {
            // check parit
#if CONFIG_HAL_BOARD != HAL_BOARD_SITL_WIN
            if (__builtin_parity((softserial->state.byte>>1)&0xFF) != (softserial->state.byte&0x200)>>9) {
#else
            if (cal_parity((softserial->state.byte>>1)&0xFF) != (softserial->state.byte&0x200)>>9) {
#endif
                goto reset;
            }
        }

        *byte = ((softserial->state.byte>>1) & 0xFF);
        softserial->state.byte >>= softserial->byte_width;
        softserial->state.bit_ofs -= softserial->byte_width;
        if (softserial->state.bit_ofs > softserial->byte_width) {
            softserial->state.byte = 0;
            softserial->state.bit_ofs = bits_low;
        }
        // swallow idle bits
        while (softserial->state.bit_ofs > 0 && (softserial->state.byte & 1)) {
            softserial->state.bit_ofs--;
            softserial->state.byte >>= 1;
        }
        return true;
    }
    return false;

reset:
    softserial->state.byte = 0;
    softserial->state.bit_ofs = 0;

    return false;
}

static uint8_t cal_parity(uint8_t ch)//奇数个1返回 1
{
    ch^=(ch>>4);
    ch^=(ch>>2);
    ch^=(ch>>1);
    ch&=0x01;

    return(ch);
}

/*------------------------------------test------------------------------------*/


